'''
Created on 13-Jul-2012

@author: Shashank.Ashtikar

================================================================================
DESCRIPTOR Protocol:
descr.__get__(self, obj, type=None) --> value

descr.__set__(self, obj, value) --> None

descr.__delete__(self, obj) --> None

If an object defines any of these methods and an object is considered a descriptor
and can override default behaviour upon being looked up as an attribute.
================================================================================

In general a descriptor is an object attribute with binding behaviour, one whose 
attribute access has been overridden by methods defined by descriptor protocol

These methods are __get__(), __set__(), __delete__() if ANY OF THESE methods are
defined for an object, it is said to be a descriptor. 

How descriptor works:
The default behaviour for attribute access is to get the attribute from an 
objects dictionary for instance a.x has a lookup chain as follows
Step 1  a.__dict__['X']
Step 2 type(a).__dict__['X'], this is continued through all the base classes.

If looked up values is an object defining one of the descriptor methods then
pytohn MAY override this default attribute lookup chain and invoke descriptor 
method Where this occurs in the precedence chain depends upon which descriptor
methods were defined.

Descriptros are only invoked on new style classes / objects. A new style class
can be defined by inheriting it from object or type)


'''

class PAttribute(object):
    """
    Descriptor
    """
    
    def __init__(self, name='var', value=''):
        self.val = value
        self.name = name
        
    def __get__(self, obj, objtype):
        """
        Avoid print obj and objtype here since it may result in recursive
        call to (__get__) method.
        """
        return self.val
    
    def __set__(self, obj, val):
        """
        Avoid print obj and objtype here since it may result in recursive
        call to (__get__) method.
        """

        self.val = val
        
    def __delete__(self, obj):
        print obj
        print "Hell no you can not delete this attribute"
        return None
    

class Person(object):
    """
    An example showing all the descriptor methods
    An object called person as a descriptor
    """
    fname = PAttribute('fname', 'Silly')
    lname = PAttribute('lname', 'Man')
    
    
    def __str__(self):
        msg = []
        for attr in dir(self):
            print attr, type(attr)
            if isinstance(attr, PAttribute):
                msg.append(attr)
        print msg 
        return '<Person: %s %s>'%(self.fname, self.lname)
    
            
    def __delete__(self, obj):
        return None
    


if __name__ == '__main__':
    p = Person()
    print p.fname, p.lname
    p.fname = 'Idiot'
    p.lname = 'Man'
    print p.fname, p.lname
    del p.fname    
    print p.fname
    
    print '===================================='
    print p
    print '===================================='